export abstract class DisplayObject {

    x = 0;
    y = 0;
    alpha = 1;

    draw(context: CanvasRenderingContext2D) {
        context.globalAlpha = this.alpha;
        this.render(context);
    }

    abstract render(context: CanvasRenderingContext2D): void

}


export class Bitmap extends DisplayObject {

    source: string;

    render(context: CanvasRenderingContext2D) {
        const img = getImage(this.source);
        if (img) {
            context.drawImage(img, this.x, this.y);
        }
    }
}

export class Rectangle extends DisplayObject {

    color = '#000000'
    width = 100;
    height = 100;

    render(context: CanvasRenderingContext2D) {
        context.save();
        context.fillStyle = this.color;
        context.fillRect(this.x, this.y, this.width, this.height);
        context.restore();
    }
}


export class TextField extends DisplayObject {


    color = '#000000'
    text = '';

    render(context: CanvasRenderingContext2D) {
        context.save();
        context.fillStyle = this.color;
        context.fillText(this.text, this.x, this.y + 20);
        context.restore();
    }
}

const store: any = {};



function getImage(url: string) {
    return store[url];
}

function loadMultiImage(list: string[], onSuccess: Function) {
    let count = 0;
    for (const url of list) {
        loadImage(url, function onLoadItem(img) {
            count++;
            store[url] = img;
            if (count === list.length) {
                onSuccess();
            }
        })
    }
}

function loadImage(url: string, onSuccess: Function) {
    const img = new Image();
    img.onload = function () {
        onSuccess(img);
    }
    //异步过程
    img.src = url;
}


export class GameEngine {
    objects: { [id: string]: DisplayObject } = {};

    private displayObject: DisplayObject[] = [];

    onStart?: () => void;
    onUpdate?: (duringTime: number) => void;
    onTick?: () => void;

    start(images: string[], sceneConfig: any, onComplete?: Function) {
        const initialTime = Date.now();
        loadMultiImage(images, () => {
            this.loadScene(sceneConfig);
            if (this.onStart) {
                this.onStart();
            }
            const advancedTime = Date.now() - initialTime;
            this.enterFrame(advancedTime);
            if (onComplete) {
                onComplete();
            }


        });
    }

    private lastTime = 0;

    enterFrame(advancedTime: number) {
        const duringTime = advancedTime - this.lastTime;
        this.lastTime = advancedTime;
        this.onEnterFrame(duringTime);
        requestAnimationFrame((advancedTime) => this.enterFrame(advancedTime));
    }


    private iterationCurrentTime = 0;
    /**
     * 在每一帧执行 onTick，其中包含如下内容：
     * 1. 清空屏幕
     * 2. 执行业务逻辑
     * 3. 执行渲染
     */
    private onEnterFrame(duringTime: number) {
        const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
        const context = canvas.getContext('2d');
        context.clearRect(0, 0, canvas.width, canvas.height);

        this.iterationCurrentTime += duringTime;
        const fps = 60;
        const mileSecondPerTick = 1000 / fps;
        while (this.iterationCurrentTime >= mileSecondPerTick) {
            this.iterationCurrentTime -= mileSecondPerTick;
            if (this.onTick) {
                this.onTick();
            }
        }
        if (this.onUpdate) {
            this.onUpdate(duringTime);
        }
        this.draw(context);
    }

    getDisplayObject(id: string) {
        return this.objects[id];
    }

    addDisplayObject(displayObject: DisplayObject) {
        this.displayObject.push(displayObject)
    }

    draw(context: CanvasRenderingContext2D) {
        for (const displayObject of this.displayObject) {
            displayObject.draw(context);
        }
    }

    loadScene(sceneConfig: any) {
        for (let config of sceneConfig) {
            const object = createDisplayObject(config.type, config.properties);
            if (config.id) {
                this.objects[config.id] = object;
            }
            this.addDisplayObject(object);
        }
    }


}


function createDisplayObject(type: string, properties: any): DisplayObject {
    let displayObject: DisplayObject;
    switch (type) {
        case 'rectangle':
            const rect = new Rectangle();
            rect.color = properties.color = '0x000000';
            displayObject = rect;
            break;
        case 'textfield':
            const textfield = new TextField();
            textfield.color = properties.color || '0x000000';
            textfield.text = properties.text || '';
            displayObject = textfield;
            break;
        case 'bitmap':
            const bitmap = new Bitmap();
            bitmap.source = properties.source || '';
            displayObject = bitmap;

    }
    displayObject.x = properties.x || 0;
    displayObject.y = properties.y || 0;
    displayObject.alpha = properties.hasOwnProperty('alpha') ? properties.alpha : 1;
    return displayObject;
}
